package com.klikmee.sms.core.base;

import java.text.MessageFormat;
import java.util.Map;

import org.apache.log4j.Logger;

import com.klikmee.sms.core.base.communicator.DeliveryReport;
import com.klikmee.sms.core.base.communicator.DeliveryReport2MessageResolver;
import com.klikmee.sms.core.base.exceptions.DeliveryReportNotResolvedException;
import com.klikmee.sms.core.base.handlers.DeliveryReportHandler;
import com.klikmee.sms.core.base.postman.ServiceMessageDeliverer;
import com.klikmee.sms.core.commons.filters.ProcessItemFilter;

public abstract class DeliveryReportFilterBase<T> implements ProcessItemFilter<DeliveryReport> {

	private Logger												logger						= Logger.getLogger( DeliveryReportFilterBase.class );
	/**
	 * Map of delivery report resolvers used to resolve delivery report to send
	 * message. Every {@link DeliveryReport2MessageResolver} instance is mapped
	 * to provider's unique name.
	 */
	protected Map<String, DeliveryReport2MessageResolver<T>>	deliveryReportResolverMap	= null;
	/**
	 * Instance of delivery report handler for charged messages
	 */
	protected DeliveryReportHandler<T>							deliveryReportHandler;
	/**
	 * Instance of message deliverer used to forward delivery reports to service
	 * instances
	 */
	protected ServiceMessageDeliverer							serviceMessageDeliverer;

	public DeliveryReportFilterBase() {
		super();
	}

	public void init() {
		logger.debug( MessageFormat.format( "Registered [{0}] delivery resolvers", deliveryReportResolverMap.size() ) );
		logger.debug( MessageFormat.format( "Using [{0}] as delivery report handler", deliveryReportHandler ) );
		logger.info( "Charged delivery report filter initialized" );
	}

	@Override
	public boolean process(DeliveryReport deliveryReport) {
		boolean deliveryReportProcessed = false;

		DeliveryReport2MessageResolver<T> deliveryReport2MessageResolver = getResolver( deliveryReport );
		// try to resolve sent message
		try {
			// resolve delivery report ot message
			T sentMessage = resolve( deliveryReport, deliveryReport2MessageResolver );
			// update message's status from delivery report
			updateStatus( deliveryReport, sentMessage );
			// handing over to handler for staus and state updates
			this.deliveryReportHandler.handle( sentMessage );
			logger.debug( MessageFormat.format( "Delivery report for [{0}] handled", sentMessage ) );
			// post delivery report to service instance
			postReportToService( sentMessage );
			// if all goes well, return positive result to item processor
			deliveryReportProcessed = true;
		}
		catch (DeliveryReportNotResolvedException e) {
			// this means that resolver could not resolve sent message, usually
			// due to this was a delivery report for bulk message or there are
			// pending database operations
			// just log it for debugging
			logger.debug( MessageFormat.format( "Could not resolve charged message for delivery report data: {0}", deliveryReport ) );
		}
		catch (Exception e) {
			logger.fatal( "Unexpected error while processing charged delivery report", e );
		}
		return deliveryReportProcessed;
	}

	protected abstract void postReportToService(T sentMessage);

	protected abstract void updateStatus(DeliveryReport deliveryReport, T sentMessage);

	protected T resolve(DeliveryReport deliveryReport, DeliveryReport2MessageResolver<T> deliveryReport2MessageResolver) throws DeliveryReportNotResolvedException {
		// this the actual message to be found using data from parameter
		T sentMessage = deliveryReport2MessageResolver.resolve( deliveryReport );
		logger.debug( MessageFormat.format( "Resoved delivery report to message [{0}]", sentMessage ) );
		return sentMessage;
	}

	protected DeliveryReport2MessageResolver<T> getResolver(DeliveryReport deliveryReport) {
		// get delivery report resolver for privier
		DeliveryReport2MessageResolver<T> deliveryReport2MessageResolver = deliveryReportResolverMap.get( deliveryReport.getProvider().getName() );
		logger.debug( MessageFormat.format( "Using [{0}] delivery resolver", deliveryReport2MessageResolver ) );
		return deliveryReport2MessageResolver;
	}

	public Map<String, DeliveryReport2MessageResolver<T>> getDeliveryReportResolverMap() {
		return deliveryReportResolverMap;
	}

	public void setDeliveryReportResolverMap(Map<String, DeliveryReport2MessageResolver<T>> deliveryReportResolverMap) {
		this.deliveryReportResolverMap = deliveryReportResolverMap;
	}

	public DeliveryReportHandler<T> getDeliveryReportHandler() {
		return deliveryReportHandler;
	}

	public void setDeliveryReportHandler(DeliveryReportHandler<T> deliveryReportHandler) {
		this.deliveryReportHandler = deliveryReportHandler;
	}

	public void setServiceMessageDeliverer(ServiceMessageDeliverer serviceMessageDeliverer) {
		this.serviceMessageDeliverer = serviceMessageDeliverer;
	}

}